package indigo.util;

import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;

import java.lang.reflect.Method;
import java.util.function.Function;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class PropertyName<E> {
	private final E inspector;

	@SuppressWarnings("unchecked")
	private PropertyName(Class<E> entityType) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(entityType);
		enhancer.setCallback(new GetterMethodInterceptor());
		this.inspector = (E) enhancer.create();
	}

	public static <E> PropertyName<E> from(Class<E> entityType) {
		return new PropertyName<>(entityType);
	}

	public String of(Function<E, ?> getter) {
		try {
			getter.apply(inspector);
		} catch (PropertyMethodInvoked pmi) {
			return pmi.getPropertyName();
		}
		throw new IllegalArgumentException("Not a property getter");
	}

	private static class GetterMethodInterceptor implements MethodInterceptor {
		@Override
		public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
			String methodName = method.getName();
			if (methodName.startsWith("get") && methodName.length() > 3) {
				String propertyName = UPPER_CAMEL.to(LOWER_CAMEL, methodName.substring(3));
				throw new PropertyMethodInvoked(propertyName);
			}
			return null;
		}
	}

	private static class PropertyMethodInvoked extends RuntimeException {
		private static final long serialVersionUID = -2682810764544753925L;

		private final String propertyName;

		public PropertyMethodInvoked(String propertyName) {
			this.propertyName = propertyName;
		}

		public String getPropertyName() {
			return propertyName;
		}
	}
}
